﻿using System;
using System.Data;
using System.Data.Entity;

namespace Model.Manager.DataBase.EntityFramework
{
    /// <summary>
    /// EntityFramewrok 框架数据库容器
    ///     revsion:1.1
    ///     desc:重做释放方式
    /// </summary>
    public class DatabaseContext : IDbContext
    {
        /// <summary>
        ///     EF4.X的数据库容器
        /// </summary>
        private readonly DbContext _context;

        /// <summary>
        ///     对象操作与查询执行类
        /// </summary>
        internal Operator BaseOperator = null;

        /// <summary>
        ///     单例模式，保证每次实例化context有且只有一个IOperator对象
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <returns></returns>
        private EntityOperator<TEntity> Singleton<TEntity>() where TEntity : class
        {
            if (NeedConstructNewOperator<TEntity>())
            {
                BaseOperator = new EntityOperator<TEntity>(_context);
            }
            return (EntityOperator<TEntity>)BaseOperator;
        }

        /// <summary>
        ///     当前对象为空
        ///     当前对象的与要生成的对象类型不相匹配
        /// 生成对象
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <returns></returns>
        private bool NeedConstructNewOperator<TEntity>() where TEntity : class
        {
            return BaseOperator == null || typeof(EntityOperator<TEntity>) != BaseOperator.GetType();
        }

        /// <summary>
        ///     初始构造必须传入数据库容器
        /// </summary>
        /// <param name="context"></param>
        public DatabaseContext(DbContext context)
        {
            _context = context;
        }

        /// <summary>
        ///     数据操作与查询入口：获取数据操作类的唯一途径
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <returns></returns>
        public IOperator<TEntity> GetOperator<TEntity>()
            where TEntity : class
        {
            return Singleton<TEntity>();
        }

        /// <summary>
        ///     数据查询入口：获取数据查询IQueryable
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <returns></returns>
        public System.Linq.IQueryable<TEntity> Queryable<TEntity>() where TEntity : class
        {
            return Singleton<TEntity>().CreateQuery();
        }

        /// <summary>
        ///     隐性事务
        /// </summary>
        /// <returns></returns>
        public int SaveAllChange()
        {
            return _context.SaveChanges();
        }


        #region 释放
        /************************************************
         * Dispose(bool)是真正释放资源的地方，参数disposing表明是用户主动释放资源还是在垃圾回收时释放。
         * 当用户主动释放资源时，由于此对象的成员中，某些托管资源持有非托管资源，因此需要释放这些托管资源，如果这些托管资源实现了IDisposable,调用Dispose()即可。
         * 在垃圾回收时，由于无法确定这些托管资源是否已经被回收了，因此不要试图释放托管资源，GC会释放这些资源的。
         * 如果多次调用Dispose(bool)会有副作用，则需要添加disposed变量来判断是否已经释放了资源。
         * 如果对象可能会在多线程环境中使用，需要加锁防止同步问题。
         * 
         */


        /// <summary>
        ///     释放资源，不含事务提交
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        private bool _mDisposed;

        protected virtual void Dispose(bool disposing)
        {
            if (!_mDisposed)
            {
                if (disposing)
                {
                    //由于此对象的成员中，某些托管资源持有非托管资源，因此需要释放这些托管资源，如果这些托管资源实现了IDisposable,调用Dispose()即可
                    if (BaseOperator != null)
                        BaseOperator.Close();// Release managed resources

                    if (_context != null)
                        _context.Dispose();
                }
                //在垃圾回收时，由于无法确定这些托管资源是否已经被回收了，因此不要试图释放托管资源，GC会释放这些资源的。
                // Release unmanaged resources

                _mDisposed = true;
            }
        }

        ~DatabaseContext()
        {
            Dispose(false);
        }
        #endregion


        #region 显式事务 暂未使用

        private IDbTransaction _tran;

        public void BeginTran()
        {
            _tran = _context.Database.Connection.BeginTransaction();
        }

        public void CommitTran()
        {
            _tran.Commit();
        }

        public void RollbackTran()
        {
            _tran.Rollback();
        }

        #endregion



    }
}